<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  
  

  
  
  
  
  
  
  <title>南辞的技术博客</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta property="og:type" content="website">
<meta property="og:title" content="南辞的技术博客">
<meta property="og:url" content="https://yanlidoushikeke.gitee.io/myhexo/page/2/index.html">
<meta property="og:site_name" content="南辞的技术博客">
<meta property="og:locale" content="en_US">
<meta property="article:author" content="南辞">
<meta name="twitter:card" content="summary">
  
  
    <link rel="icon" href="/tefuir/css/images/favicon.ico">
  
  
<link rel="stylesheet" href="/tefuir/css/style.css">

  

  
  <!-- baidu webmaster push -->
  <script src='//push.zhanzhang.baidu.com/push.js'></script>
<meta name="generator" content="Hexo 5.2.0"><link rel="alternate" href="/tefuir/atom.xml" title="南辞的技术博客" type="application/atom+xml">
</head>
<body class="home blog custom-background custom-font-enabled single-author">
  <div id="page" class="hfeed site">
      <header id="masthead" class="site-header" role="banner">
    <hgroup>
      <h1 class="site-title">
        <a href="/tefuir/" title="南辞的技术博客" rel="home">南辞的技术博客</a>
      </h1>
      
        <h2 class="site-description hitokoto"></h2>
        <script type="text/javascript" src="https://v1.hitokoto.cn/?encode=js"></script>
      
    </hgroup>

    <nav id="site-navigation" class="main-navigation" role="navigation">
            <button class="menu-toggle">菜单</button>
            <a class="assistive-text" href="/#content" title="跳至内容">跳至内容</a><!--TODO-->
            <div class="menu-main-container">
                <ul id="menu-main" class="nav-menu">
                
                    <li class="menu-item menu-item-type-post_type menu-item-object-page"><a href="/tefuir/">Home</a></li>
                
                    <li class="menu-item menu-item-type-post_type menu-item-object-page"><a href="/tefuir/archives">Archives</a></li>
                
                </ul>
            </div>
    </nav>
</header>

      <div id="main" class="wrapper">
        <div id="primary" class="site-content"><div id="content" role="main">
  
    <article id="post-进度条" class="post-进度条 post type-post status-publish format-standard hentry">
    <!---->

      <header class="entry-header">
        
        
  
    <h1 class="entry-title">
      <a class="article-title" href="/tefuir/2021/03/24/%E8%BF%9B%E5%BA%A6%E6%9D%A1/">进度条</a>
    </h1>
  

        
        <div class="comments-link">
            
            <a href="javascript:void(0);" data-url="https://yanlidoushikeke.gitee.io/myhexo/2021/03/24/%E8%BF%9B%E5%BA%A6%E6%9D%A1/" data-id="ckmn4ijy10000k0u07iud1vne" class="leave-reply bdsharebuttonbox" data-cmd="more">Share</a>
        </div><!-- .comments-link -->
      </header><!-- .entry-header -->

    <div class="entry-content">
      
        <p>任务进度可视化，python进度条模块Tqdm</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Tqdm是一个易用性强,拓展性高的python进度条库，可以在Python长循环中添加一个进度提示信息，我们只需要封装任意的迭代器即可。</span><br></pre></td></tr></table></figure>

<h4 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install tqdm</span><br></pre></td></tr></table></figure>

<h4 id="自动控制更新速度"><a href="#自动控制更新速度" class="headerlink" title="自动控制更新速度"></a>自动控制更新速度</h4><p>代码如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">import time</span><br><span class="line">from tqdm import *</span><br><span class="line">for i in trange(100):</span><br><span class="line">    time.sleep(0.1)</span><br></pre></td></tr></table></figure>

<h4 id="使用方法trange"><a href="#使用方法trange" class="headerlink" title="使用方法trange"></a>使用方法trange</h4><p>trange(i)是Sqdm(range(i))的简单写法。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">import time</span><br><span class="line">from tqdm import *</span><br><span class="line">for i in trange(100):</span><br><span class="line">    time.sleep(0.1)</span><br></pre></td></tr></table></figure>

<h4 id="为进度条设置描述"><a href="#为进度条设置描述" class="headerlink" title="为进度条设置描述"></a>为进度条设置描述</h4><p>在for循环外部初始化tqdm,可以作为信息打印在进度条中</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">import time</span><br><span class="line">from tqdm import *</span><br><span class="line"></span><br><span class="line">pdar &#x3D; tqdm([&#39;download&#39;,&#39;install&#39;,&#39;debug&#39;,&#39;run&#39;])</span><br><span class="line">for char in pdar:</span><br><span class="line">    with open(&#39;a.txt&#39;,&#39;w&#39;) as w:</span><br><span class="line">        w.write(char)</span><br><span class="line"></span><br><span class="line">    pdar.set_description(&#39;Processing %s&#39;% char )</span><br><span class="line">    time.sleep(1)</span><br></pre></td></tr></table></figure>



<p>另外，在使用tqdm显示进度条的时，若代码中存在print可能会使得输出杂乱，此时可以将print语句替换为tqdm.write，代码如下:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">import time</span><br><span class="line">from tqdm import *</span><br><span class="line">for i in trange(10):</span><br><span class="line">    time.sleep(0.1)</span><br><span class="line">    if not (i % 2):</span><br><span class="line">        tqdm.write(&#39;Done task &#123;0&#125;&#39;.format(i))</span><br><span class="line"></span><br></pre></td></tr></table></figure>




      
    </div><!-- .entry-content -->

    <footer class="entry-meta">
    <a href="/tefuir/2021/03/24/%E8%BF%9B%E5%BA%A6%E6%9D%A1/">
    <time datetime="2021-03-24T06:23:45.000Z" class="entry-date">
        2021-03-24
    </time>
</a>
    
    
    </footer>
</article>






  
    <article id="post-消息撤回" class="post-消息撤回 post type-post status-publish format-standard hentry">
    <!---->

      <header class="entry-header">
        
        
  
    <h1 class="entry-title">
      <a class="article-title" href="/tefuir/2021/03/23/%E6%B6%88%E6%81%AF%E6%92%A4%E5%9B%9E/">消息撤回</a>
    </h1>
  

        
        <div class="comments-link">
            
            <a href="javascript:void(0);" data-url="https://yanlidoushikeke.gitee.io/myhexo/2021/03/23/%E6%B6%88%E6%81%AF%E6%92%A4%E5%9B%9E/" data-id="ckmlt5cd30000bku06svu8aan" class="leave-reply bdsharebuttonbox" data-cmd="more">Share</a>
        </div><!-- .comments-link -->
      </header><!-- .entry-header -->

    <div class="entry-content">
      
        <p>如今微信是个简洁并且功能强大的社交App，那么微信撤回消息的功能究竟是如何实现的呢，撤回的消息我们能不能查看呢，今天78行代码实现微信撤回消息查看   —Python itchat</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">下面就使用itchat做一个这样的程序：</span><br><span class="line">私聊撤回的信息可以收集起来并发送到个人微信的文件助手，包括：</span><br><span class="line">(1) who ：谁发送的</span><br><span class="line">(2) when ：什么时候发送的消息</span><br><span class="line">(3) what：什么信息</span><br><span class="line">(4) which：哪一类信息，包括：文本、图片、语音、视频、分享、位置、附件</span><br></pre></td></tr></table></figure>

<h4 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br></pre></td><td class="code"><pre><span class="line"># -*-encoding:utf-8-*-</span><br><span class="line">import os</span><br><span class="line">import re</span><br><span class="line">import shutil</span><br><span class="line">import time</span><br><span class="line">import itchat</span><br><span class="line">from itchat.content import *</span><br><span class="line"></span><br><span class="line"># 说明：可以撤回的有文本文字、语音、视频、图片、位置、名片、分享、附件</span><br><span class="line"></span><br><span class="line"># &#123;msg_id:(msg_from,msg_to,msg_time,msg_time_rec,msg_type,msg_content,msg_share_url)&#125;</span><br><span class="line">msg_dict &#x3D; &#123;&#125;</span><br><span class="line"></span><br><span class="line"># 文件存储临时目录</span><br><span class="line">rev_tmp_dir &#x3D; &quot;&#x2F;home&#x2F;alic&#x2F;RevDir&#x2F;&quot;</span><br><span class="line">if not os.path.exists(rev_tmp_dir): os.mkdir(rev_tmp_dir)</span><br><span class="line"></span><br><span class="line"># 表情有一个问题 | 接受信息和接受note的msg_id不一致 巧合解决方案</span><br><span class="line">face_bug &#x3D; None</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 将接收到的消息存放在字典中，当接收到新消息时对字典中超时的消息进行清理 | 不接受不具有撤回功能的信息</span><br><span class="line"># [TEXT, PICTURE, MAP, CARD, SHARING, RECORDING, ATTACHMENT, VIDEO, FRIENDS, NOTE]</span><br><span class="line">@itchat.msg_register([TEXT, PICTURE, MAP, CARD, SHARING, RECORDING, ATTACHMENT, VIDEO])</span><br><span class="line">def handler_receive_msg(msg):</span><br><span class="line">    global face_bug</span><br><span class="line">    # 获取的是本地时间戳并格式化本地时间戳 e: 2017-04-21 21:30:08</span><br><span class="line">    msg_time_rec &#x3D; time.strftime(&quot;%Y-%m-%d %H:%M:%S&quot;, time.localtime())</span><br><span class="line">    # 消息ID</span><br><span class="line">    msg_id &#x3D; msg[&#39;MsgId&#39;]</span><br><span class="line">    # 消息时间</span><br><span class="line">    msg_time &#x3D; msg[&#39;CreateTime&#39;]</span><br><span class="line">    # 消息发送人昵称 | 这里也可以使用RemarkName备注　但是自己或者没有备注的人为None</span><br><span class="line">    msg_from &#x3D; (itchat.search_friends(userName&#x3D;msg[&#39;FromUserName&#39;]))[&quot;NickName&quot;]</span><br><span class="line">    # 消息内容</span><br><span class="line">    msg_content &#x3D; None</span><br><span class="line">    # 分享的链接</span><br><span class="line">    msg_share_url &#x3D; None</span><br><span class="line">    if msg[&#39;Type&#39;] &#x3D;&#x3D; &#39;Text&#39; \</span><br><span class="line">            or msg[&#39;Type&#39;] &#x3D;&#x3D; &#39;Friends&#39;:</span><br><span class="line">        msg_content &#x3D; msg[&#39;Text&#39;]</span><br><span class="line">    elif msg[&#39;Type&#39;] &#x3D;&#x3D; &#39;Recording&#39; \</span><br><span class="line">            or msg[&#39;Type&#39;] &#x3D;&#x3D; &#39;Attachment&#39; \</span><br><span class="line">            or msg[&#39;Type&#39;] &#x3D;&#x3D; &#39;Video&#39; \</span><br><span class="line">            or msg[&#39;Type&#39;] &#x3D;&#x3D; &#39;Picture&#39;:</span><br><span class="line">        msg_content &#x3D; r&quot;&quot; + msg[&#39;FileName&#39;]</span><br><span class="line">        # 保存文件</span><br><span class="line">        msg[&#39;Text&#39;](rev_tmp_dir + msg[&#39;FileName&#39;])</span><br><span class="line">    elif msg[&#39;Type&#39;] &#x3D;&#x3D; &#39;Card&#39;:</span><br><span class="line">        msg_content &#x3D; msg[&#39;RecommendInfo&#39;][&#39;NickName&#39;] + r&quot; 的名片&quot;</span><br><span class="line">    elif msg[&#39;Type&#39;] &#x3D;&#x3D; &#39;Map&#39;:</span><br><span class="line">        x, y, location &#x3D; re.search(</span><br><span class="line">            &quot;&lt;location x&#x3D;\&quot;(.*?)\&quot; y&#x3D;\&quot;(.*?)\&quot;.*label&#x3D;\&quot;(.*?)\&quot;.*&quot;, msg[&#39;OriContent&#39;]).group(1, 2, 3)</span><br><span class="line">        if location is None:</span><br><span class="line">            msg_content &#x3D; r&quot;纬度-&gt;&quot; + x.__str__() + &quot; 经度-&gt;&quot; + y.__str__()</span><br><span class="line">        else:</span><br><span class="line">            msg_content &#x3D; r&quot;&quot; + location</span><br><span class="line">    elif msg[&#39;Type&#39;] &#x3D;&#x3D; &#39;Sharing&#39;:</span><br><span class="line">        msg_content &#x3D; msg[&#39;Text&#39;]</span><br><span class="line">        msg_share_url &#x3D; msg[&#39;Url&#39;]</span><br><span class="line">    face_bug &#x3D; msg_content</span><br><span class="line">    # 更新字典</span><br><span class="line">    msg_dict.update(</span><br><span class="line">        &#123;</span><br><span class="line">            msg_id: &#123;</span><br><span class="line">                &quot;msg_from&quot;: msg_from, &quot;msg_time&quot;: msg_time, &quot;msg_time_rec&quot;: msg_time_rec,</span><br><span class="line">                &quot;msg_type&quot;: msg[&quot;Type&quot;],</span><br><span class="line">                &quot;msg_content&quot;: msg_content, &quot;msg_share_url&quot;: msg_share_url</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    )</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 收到note通知类消息，判断是不是撤回并进行相应操作</span><br><span class="line">@itchat.msg_register([NOTE])</span><br><span class="line">def send_msg_helper(msg):</span><br><span class="line">    global face_bug</span><br><span class="line">    if re.search(r&quot;\&lt;\!\[CDATA\[.*撤回了一条消息\]\]\&gt;&quot;, msg[&#39;Content&#39;]) is not None:</span><br><span class="line">        # 获取消息的id</span><br><span class="line">        old_msg_id &#x3D; re.search(&quot;\&lt;msgid\&gt;(.*?)\&lt;\&#x2F;msgid\&gt;&quot;, msg[&#39;Content&#39;]).group(1)</span><br><span class="line">        old_msg &#x3D; msg_dict.get(old_msg_id, &#123;&#125;)</span><br><span class="line">        if len(old_msg_id) &lt; 11:</span><br><span class="line">            itchat.send_file(rev_tmp_dir + face_bug, toUserName&#x3D;&#39;filehelper&#39;)</span><br><span class="line">            os.remove(rev_tmp_dir + face_bug)</span><br><span class="line">        else:</span><br><span class="line">            msg_body &#x3D; &quot;告诉你一个秘密~&quot; + &quot;\n&quot; \</span><br><span class="line">                       + old_msg.get(&#39;msg_from&#39;) + &quot; 撤回了 &quot; + old_msg.get(&quot;msg_type&quot;) + &quot; 消息&quot; + &quot;\n&quot; \</span><br><span class="line">                       + old_msg.get(&#39;msg_time_rec&#39;) + &quot;\n&quot; \</span><br><span class="line">                       + &quot;撤回了什么 ⇣&quot; + &quot;\n&quot; \</span><br><span class="line">                       + r&quot;&quot; + old_msg.get(&#39;msg_content&#39;)</span><br><span class="line">            # 如果是分享存在链接</span><br><span class="line">            if old_msg[&#39;msg_type&#39;] &#x3D;&#x3D; &quot;Sharing&quot;: msg_body +&#x3D; &quot;\n就是这个链接➣ &quot; + old_msg.get(&#39;msg_share_url&#39;)</span><br><span class="line"></span><br><span class="line">            # 将撤回消息发送到文件助手</span><br><span class="line">            itchat.send(msg_body, toUserName&#x3D;&#39;filehelper&#39;)</span><br><span class="line">            # 有文件的话也要将文件发送回去</span><br><span class="line">            if old_msg[&quot;msg_type&quot;] &#x3D;&#x3D; &quot;Picture&quot; \</span><br><span class="line">                    or old_msg[&quot;msg_type&quot;] &#x3D;&#x3D; &quot;Recording&quot; \</span><br><span class="line">                    or old_msg[&quot;msg_type&quot;] &#x3D;&#x3D; &quot;Video&quot; \</span><br><span class="line">                    or old_msg[&quot;msg_type&quot;] &#x3D;&#x3D; &quot;Attachment&quot;:</span><br><span class="line">                file &#x3D; &#39;@fil@%s&#39; % (rev_tmp_dir + old_msg[&#39;msg_content&#39;])</span><br><span class="line">                itchat.send(msg&#x3D;file, toUserName&#x3D;&#39;filehelper&#39;)</span><br><span class="line">                os.remove(rev_tmp_dir + old_msg[&#39;msg_content&#39;])</span><br><span class="line">            # 删除字典旧消息</span><br><span class="line">            msg_dict.pop(old_msg_id)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">if __name__ &#x3D;&#x3D; &#39;__main__&#39;:</span><br><span class="line">    itchat.auto_login(hotReload&#x3D;True,enableCmdQR&#x3D;2)</span><br><span class="line">    itchat.run()</span><br></pre></td></tr></table></figure>

<p>该程序可以直接在终端运行，在终端扫码成功够即可登录成功，同时也可以打包在window系统运行(注意修改一下路径，推荐使用相对路径。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜  ~ python wx.py</span><br><span class="line">Getting uuid of QR code.</span><br><span class="line">Downloading QR code.</span><br><span class="line">Please scan the QR code to log in.</span><br><span class="line">Please press confirm on your phone.</span><br><span class="line">Loading the contact, this may take a little while.</span><br><span class="line">�[3;J</span><br><span class="line">Login successfully as AlicFeng</span><br><span class="line">Start auto replying.</span><br></pre></td></tr></table></figure>

<h4 id="tchat"><a href="#tchat" class="headerlink" title="tchat"></a>tchat</h4><p>简介<br>itchat是一个开源的微信个人号接口，使用python调用微信变得非常简单。简单是用itchat代码即可构建一个基于微信的即时通讯，更不错的体现在于方便扩展个人微信的在其他平台的更多通讯功能。</p>
<ul>
<li>安装</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip3 install itchat</span><br></pre></td></tr></table></figure>

<ul>
<li>itchat - Helloworld<br>仅仅三行代码发送一条信息给文件助手</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">import itchat</span><br><span class="line">itchat.auto_login(hotReload&#x3D;True)</span><br><span class="line">itchat.send(&#39;Hello AlicFeng&#39;, toUserName&#x3D;&#39;filehelper&#39;)</span><br></pre></td></tr></table></figure>

<ul>
<li>查看客户端</li>
</ul>

      
    </div><!-- .entry-content -->

    <footer class="entry-meta">
    <a href="/tefuir/2021/03/23/%E6%B6%88%E6%81%AF%E6%92%A4%E5%9B%9E/">
    <time datetime="2021-03-23T08:54:49.000Z" class="entry-date">
        2021-03-23
    </time>
</a>
    
    
    </footer>
</article>






  
    <article id="post-设计模式" class="post-设计模式 post type-post status-publish format-standard hentry">
    <!---->

      <header class="entry-header">
        
        
  
    <h1 class="entry-title">
      <a class="article-title" href="/tefuir/2021/03/22/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/">设计模式</a>
    </h1>
  

        
        <div class="comments-link">
            
            <a href="javascript:void(0);" data-url="https://yanlidoushikeke.gitee.io/myhexo/2021/03/22/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/" data-id="ckmkm4erd0001s8u0b1nrch7m" class="leave-reply bdsharebuttonbox" data-cmd="more">Share</a>
        </div><!-- .comments-link -->
      </header><!-- .entry-header -->

    <div class="entry-content">
      
        <p>1.什么是设计模式<br>    设计模式，是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计 模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。</p>
<p>创建型模式，共五种：工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。<br>结构型模式，共七种：适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。<br>行为型模式，共十一种：策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令 模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。开放封闭原则（ Open Close Principle ）<br>原则思想：尽量通过扩展软件实体来解决需求变化，而不是通过修改已有的代码来完成变化<br>描述：一个软件产品在生命周期内，都会发生变化，既然变化是一个既定的事实，我们就应该在设 计的时候尽量适应这些变化，以提高项目的稳定性和灵活性。<br>优点：单一原则告诉我们，每个类都有自己负责的职责，里氏替换原则不能破坏继承关系的体系。<br>里氏代换原则（ Liskov Substitution Principle ）<br>原则思想：使用的基类可以在任何地方使用继承的子类，完美的替换基类。<br>大概意思是：子类可以扩展父类的功能，但不能改变父类原有的功能。子类可以实现父类的抽象方 法，但不能覆盖父类的非抽象方法，子类中可以增加自己特有的方法。<br>优点：增加程序的健壮性，即使增加了子类，原有的子类还可以继续运行，互不影响。<br>依赖倒转原则（ Dependence Inversion Principle ）<br>依赖倒置原则的核心思想是面向接口编程.<br>依赖倒转原则要求我们在程序代码中传递参数时或在关联关系中，尽量引用层次高的抽象层类。<br>这个是开放封闭原则的基础，具体内容是：对接口编程，依赖于抽象而不依赖于具体。<br>接口隔离原则（ Interface Segregation Principle ）<br>这个原则的意思是：使用多个隔离的接口，比使用单个接口要好。还是一个降低类之间的耦合度的 意思，从这儿我们看出，其实设计模式就是一个软件的设计思想，从大型软件架构出发，为了升级 和维护方便。所以上文中多次出现：降低依赖，降低耦合。<br>例如：支付类的接口和订单类的接口，需要把这俩个类别的接口变成俩个隔离的接口<br>迪米特法则（最少知道原则）（ Demeter Principle ）<br>原则思想：一个对象应当对其他对象有尽可能少地了解，简称类间解耦 大概意思就是一个类尽量减少自己对其他对象的依赖，原则是低耦合，高内聚，只有使各个模块之 间的耦合尽量的低，才能提高代码的复用率。<br>优点：低耦合，高内聚。<br>单一职责原则（ Principle of single responsibility ）<br>原则思想：一个方法只负责一件事情。<br>描述：单一职责原则很简单，一个方法 一个类只负责一个职责，各个职责的程序改动，不影响其 它程序。 这是常识，几乎所有程序员都会遵循这个原则。<br>优点：降低类和类的耦合，提高可读性，增加可维护性和可拓展性，降低可变性的风险。</p>
<ol start="5">
<li>单例模式</li>
<li>什么是单例<br>保证一个类只有一个实例，并且提供一个访问该全局访问点</li>
<li>那些地方用到了单例模式</li>
<li>网站的计数器，一般也是采用单例模式实现，否则难以同步。</li>
<li>应用程序的日志应用，一般都是单例模式实现，只有一个实例去操作才好，否则内容不好追加显示。</li>
<li>多线程的线程池的设计一般也是采用单例模式，因为线程池要方便对池中的线程进行控制</li>
<li>Windows 的（任务管理器）就是很典型的单例模式，他不能打开俩个</li>
<li>windows 的（回收站）也是典型的单例应用。在整个系统运行过程中，回收站只维护一个实例。</li>
<li>单例优缺点<br>优点：</li>
<li>在单例模式中，活动的单例只有一个实例，对单例类的所有实例化得到的都是相同的一个实例。这 样就防止其它对象对自己的实例化，确保所有的对象都访问一个实例</li>
<li>单例模式具有一定的伸缩性，类自己来控制实例化进程，类就在改变实例化进程上有相应的伸缩 性。</li>
<li>提供了对唯一实例的受控访问。</li>
<li>由于在系统内存中只存在一个对象，因此可以节约系统资源，当需要频繁创建和销毁的对象时单例 模式无疑可以提高系统的性能。</li>
<li>允许可变数目的实例。</li>
<li>避免对共享资源的多重占用。<br>缺点：</li>
<li>不适用于变化的对象，如果同一类型的对象总是要在不同的用例场景发生变化，单例就会引起数据 的错误，不能保存彼此的状态。</li>
<li>由于单利模式中没有抽象层，因此单例类的扩展有很大的困难。</li>
<li>单例类的职责过重，在一定程度上违背了 “ 单一职责原则 ” 。 4. 滥用单例将带来一些负面问题，如为了节省资源将数据库连接池对象设计为的单例类，可能会导致 共享连接池对象的程序过多而出现连接池溢出；如果实例化的对象长时间不被利用，系统会认为是 垃圾而被回收，这将导致对象状态的丢失。</li>
<li>单例模式使用注意事项：</li>
<li>使用时不能用反射模式创建单例，否则会实例化一个新的对象</li>
<li>使用懒单例模式时注意线程安全问题</li>
<li>饿单例模式和懒单例模式构造方法都是私有的，因而是不能被继承的，有些单例模式可以被继承 （如登记式模式）</li>
<li>单例防止反射漏洞攻击</li>
</ol>
<ol start="6">
<li>如何选择单例创建方式<br>      如果不需要延迟加载单例，可以使用枚举或者饿汉式，相对来说枚举性好于饿汉式。 如果需要延 迟加载，可以使用静态内部类或者懒汉式，相对来说静态内部类好于懒韩式。 最好使用饿汉式<br> ​</li>
</ol>

      
    </div><!-- .entry-content -->

    <footer class="entry-meta">
    <a href="/tefuir/2021/03/22/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/">
    <time datetime="2021-03-22T13:12:32.000Z" class="entry-date">
        2021-03-22
    </time>
</a>
    
    
    </footer>
</article>






  
    <article id="post-vue页面如何自动刷新" class="post-vue页面如何自动刷新 post type-post status-publish format-standard hentry">
    <!---->

      <header class="entry-header">
        
        
  
    <h1 class="entry-title">
      <a class="article-title" href="/tefuir/2021/03/19/vue%E9%A1%B5%E9%9D%A2%E5%A6%82%E4%BD%95%E8%87%AA%E5%8A%A8%E5%88%B7%E6%96%B0/">vue页面如何自动刷新</a>
    </h1>
  

        
        <div class="comments-link">
            
            <a href="javascript:void(0);" data-url="https://yanlidoushikeke.gitee.io/myhexo/2021/03/19/vue%E9%A1%B5%E9%9D%A2%E5%A6%82%E4%BD%95%E8%87%AA%E5%8A%A8%E5%88%B7%E6%96%B0/" data-id="ckmkm4er40000s8u0fjty34a5" class="leave-reply bdsharebuttonbox" data-cmd="more">Share</a>
        </div><!-- .comments-link -->
      </header><!-- .entry-header -->

    <div class="entry-content">
      
        <p>当我们在做项目时，我们需要做当前页面的刷新来达到数据更新的目的，在此大概总结了几种常用的页面刷新的方法。</p>
<p>1.window.location.reload(),是原生JS提供的方法，</p>
<p>this.$router.go(0):是vue路由里面的一种方法,</p>
<p>这两种方法都可以达到页面刷新的目的，简单粗暴，但是用户体验不好，相当于按F5刷新页面，会有短暂的白屏，相当于页面的重新载入。</p>
<p>2.通过路由跳转的方法刷新，具体思路是点击按钮跳转一个空白页，然后再马上跳回来(个人感觉比较麻烦,不实用)</p>
<p>3.控制<router-view></router-view>的显示与否，在全局组件注册一个方法，该方法控制router-view的显示与否，在子组件调用即可：</p>
<p>provide / inject 组合 方式感觉比较好用,相对的比前面几种方法好用</p>
<p>(1)先修改App.vue</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">&lt;template&gt;</span><br><span class="line">  &lt;div id=<span class="string">&quot;app&quot;</span>&gt;</span><br><span class="line">    &lt;router-view v-if=<span class="string">&quot;isRouterAlive&quot;</span>&gt;&lt;/router-view&gt;</span><br><span class="line">  &lt;/div&gt;</span><br><span class="line">&lt;/template&gt;</span><br><span class="line"> </span><br><span class="line">&lt;script&gt;</span><br><span class="line"><span class="built_in">export</span> default &#123;</span><br><span class="line">  name: <span class="string">&#x27;App&#x27;</span>,</span><br><span class="line">  <span class="function"><span class="title">provide</span></span>() &#123; // 注册一个方法</span><br><span class="line">    <span class="built_in">return</span> &#123;</span><br><span class="line">      reload: this.reload</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="function"><span class="title">data</span></span>() &#123;</span><br><span class="line">    <span class="built_in">return</span> &#123;</span><br><span class="line">      isRouterAlive: <span class="literal">true</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  methods: &#123;</span><br><span class="line">    <span class="function"><span class="title">reload</span></span>() &#123;</span><br><span class="line">      this.isRouterAlive = <span class="literal">false</span></span><br><span class="line">      this.<span class="variable">$nextTick</span>(<span class="function"><span class="title">function</span></span>() &#123;</span><br><span class="line">        this.isRouterAlive = <span class="literal">true</span></span><br><span class="line">      &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">&lt;/script&gt;</span><br></pre></td></tr></table></figure>

<p>(2) 当前需要刷新的页面</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">&lt;template&gt;</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">&lt;/template&gt;</span><br><span class="line"> </span><br><span class="line">&lt;script&gt;</span><br><span class="line">    <span class="built_in">export</span> default&#123;</span><br><span class="line">        inject: [<span class="string">&#x27;reload&#x27;</span>], // 引入方法</span><br><span class="line">        <span class="function"><span class="title">data</span></span>()&#123;</span><br><span class="line">            <span class="built_in">return</span>&#123;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="function"><span class="title">mounted</span></span>()&#123;</span><br><span class="line">　　　　　　　this.reload()  // 哪里需要在哪里调用</span><br><span class="line">        &#125;,</span><br><span class="line">        methods:&#123;&#125;</span><br><span class="line">    &#125;</span><br><span class="line">&lt;/script&gt;</span><br><span class="line"></span><br></pre></td></tr></table></figure>
      
    </div><!-- .entry-content -->

    <footer class="entry-meta">
    <a href="/tefuir/2021/03/19/vue%E9%A1%B5%E9%9D%A2%E5%A6%82%E4%BD%95%E8%87%AA%E5%8A%A8%E5%88%B7%E6%96%B0/">
    <time datetime="2021-03-19T00:40:48.000Z" class="entry-date">
        2021-03-19
    </time>
</a>
    
    
    </footer>
</article>






  
    <article id="post-时间类型错误" class="post-时间类型错误 post type-post status-publish format-standard hentry">
    <!---->

      <header class="entry-header">
        
        
  
    <h1 class="entry-title">
      <a class="article-title" href="/tefuir/2021/03/17/%E6%97%B6%E9%97%B4%E7%B1%BB%E5%9E%8B%E9%94%99%E8%AF%AF/">时间类型错误</a>
    </h1>
  

        
        <div class="comments-link">
            
            <a href="javascript:void(0);" data-url="https://yanlidoushikeke.gitee.io/myhexo/2021/03/17/%E6%97%B6%E9%97%B4%E7%B1%BB%E5%9E%8B%E9%94%99%E8%AF%AF/" data-id="ckmcu0qng0001pou0ddgt6ez7" class="leave-reply bdsharebuttonbox" data-cmd="more">Share</a>
        </div><!-- .comments-link -->
      </header><!-- .entry-header -->

    <div class="entry-content">
      
        <p> 我的描述：我在flask框架中引用orm查数据库并返回数据，出现此类问题，如下图：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">解决 - Object of type &#39;datetime&#39; is not JSON serializable</span><br></pre></td></tr></table></figure>

<p>解决方案：</p>
<p>1、从表面意思看，就是说datetime时间类型无法被序列化。于是我百度了网上的同事的解答，大多说是时间转换错误，给我提供了so mang解决方案代码，有些懵，总之意思就是有时间字段无法被解析</p>
<p>2、那就只好打断点进行调试，果不其然返回的数据有datetime类型信息，找到了原因：</p>
<p>就是重写构造json类，遇到日期特殊处理，其余的用内置的就行。</p>
<p> 3、好吧，那么我现在懂前辈们放一段代码的意思了。。。代码如下</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">json.dumps(self_data, cls&#x3D;DateEncoder)  </span><br></pre></td></tr></table></figure>



<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">import json</span><br><span class="line">import datetime</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">class DateEncoder(json.JSONEncoder):</span><br><span class="line">    def default(self, obj):</span><br><span class="line">        if isinstance(obj, datetime.datetime):</span><br><span class="line">            return obj.strftime(&#39;%Y-%m-%d %H:%M:%S&#39;)</span><br><span class="line">        elif isinstance(obj, datetime.date):</span><br><span class="line">            return obj.strftime(&quot;%Y-%m-%d&quot;)</span><br><span class="line">        else:</span><br><span class="line">            return json.JSONEncoder.default(self, obj)</span><br></pre></td></tr></table></figure>





<p> 4、再次断点测试，datetime时间类型变成字符串类型。</p>
<p> 5、不过我发现竟然有个bug，我不单单把datetime类型转成了字符串。连带着把list也转成了字符串，这可不是我想要的！还有办法。如下图：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">return HttpResponse(json.dumps(res,indent&#x3D;4,ensure_ascii&#x3D;False,cls&#x3D;DateEncoder),content_type&#x3D;&#39;application&#x2F;json&#39;)</span><br></pre></td></tr></table></figure>







      
    </div><!-- .entry-content -->

    <footer class="entry-meta">
    <a href="/tefuir/2021/03/17/%E6%97%B6%E9%97%B4%E7%B1%BB%E5%9E%8B%E9%94%99%E8%AF%AF/">
    <time datetime="2021-03-17T02:31:04.000Z" class="entry-date">
        2021-03-17
    </time>
</a>
    
    
    </footer>
</article>






  
    <article id="post-二进制" class="post-二进制 post type-post status-publish format-standard hentry">
    <!---->

      <header class="entry-header">
        
        
  
    <h1 class="entry-title">
      <a class="article-title" href="/tefuir/2021/03/15/%E4%BA%8C%E8%BF%9B%E5%88%B6/">二进制</a>
    </h1>
  

        
        <div class="comments-link">
            
            <a href="javascript:void(0);" data-url="https://yanlidoushikeke.gitee.io/myhexo/2021/03/15/%E4%BA%8C%E8%BF%9B%E5%88%B6/" data-id="ckmcu0qn20000pou00zzy71na" class="leave-reply bdsharebuttonbox" data-cmd="more">Share</a>
        </div><!-- .comments-link -->
      </header><!-- .entry-header -->

    <div class="entry-content">
      
        
      
    </div><!-- .entry-content -->

    <footer class="entry-meta">
    <a href="/tefuir/2021/03/15/%E4%BA%8C%E8%BF%9B%E5%88%B6/">
    <time datetime="2021-03-15T01:12:44.000Z" class="entry-date">
        2021-03-15
    </time>
</a>
    
    
    </footer>
</article>






  
    <article id="post-权限管理" class="post-权限管理 post type-post status-publish format-standard hentry">
    <!---->

      <header class="entry-header">
        
        
  
    <h1 class="entry-title">
      <a class="article-title" href="/tefuir/2021/03/11/%E6%9D%83%E9%99%90%E7%AE%A1%E7%90%86/">权限管理</a>
    </h1>
  

        
        <div class="comments-link">
            
            <a href="javascript:void(0);" data-url="https://yanlidoushikeke.gitee.io/myhexo/2021/03/11/%E6%9D%83%E9%99%90%E7%AE%A1%E7%90%86/" data-id="ckm4nu57y00005cu08i4m3jym" class="leave-reply bdsharebuttonbox" data-cmd="more">Share</a>
        </div><!-- .comments-link -->
      </header><!-- .entry-header -->

    <div class="entry-content">
      
        <h2 id="权限管理"><a href="#权限管理" class="headerlink" title="权限管理"></a>权限管理</h2><h2 id="1-RBAC模型"><a href="#1-RBAC模型" class="headerlink" title="1.RBAC模型"></a>1.RBAC模型</h2><p>RBAC，英文全称Role-Based Access Control，基于角色的权限访问控制，它将who、what、how进行了关联，<strong>解释了谁（who）对什么（what）做了怎么样操作（how）</strong>的问题。</p>
<p>RBAC包含了四个子模型，分别是RBAC0，RBAC1，RBAC2和RBAC3，它们的核心思想都是相同的，只是在某些地方有差异。</p>
<ul>
<li><p><strong>RBAC0</strong></p>
<p><img src="https://pic1.zhimg.com/80/v2-fccc8c8beab3e11d9f812c408d347ab4_720w.jpg" alt="img"></p>
</li>
<li><h5 id="RBAC1"><a href="#RBAC1" class="headerlink" title="RBAC1"></a>RBAC1</h5></li>
</ul>
<ul>
<li>一个角色可以拥有多个等级，**角色和等级之间是一对多的关系；</li>
<li><strong>一个角色下的不同等级之间，具有相同的权限，但又会有不同的权限，但他们都只会拥有该角色下的权限。</strong></li>
</ul>
<p>通过角色分级，可以实现更加精细化的权限分配，还是以上面的那个例子来说明：</p>
<p>王小明是公司的开发部门的经理，具有公司开发人员的的管理权。而王小亮是副经理，只具有部分管理权。此时我们就可以设置一个比经理低一等级的副经理的角色，并赋予该角色部分经理的角色即可。当有一天王小明离职了，王小亮被提拔成经理，那么可以直接将王小明的角色继承给王小亮即可。</p>
<p><img src="https://pic3.zhimg.com/80/v2-2d974dd151fd0c15760aae9cdc1cee0e_720w.jpg" alt="img">RBAC1增</p>
<ul>
<li><strong>RBAC2</strong></li>
</ul>
<p>RBAC2是在RBAC0的基础上增加了对角色和权限的一些限制。</p>
<ul>
<li><strong>角色互斥限制。</strong>限制当系统中有多个角色时，用户只能同时拥有它们中的一个角色。或者当用户拥有多个角色时，只能同时激活使用其中的一个角色。</li>
<li><strong>角色基数限制。</strong>限制用户只能同时拥有或激活的一定数量的角色，或者限制角色只能被赋予一定数量的权限。</li>
<li><strong>先决条件限制。</strong>当用户想要拥有一个角色时，必须现拥有另一个角色才可以，或者当角色需要拥有一个权限时，必须先拥有另一个权限。</li>
</ul>
<p>还是以上面的案例来说明：</p>
<p>小明拥有公司的开发部门经理的角色，此时就不会再让他拥有公司财务部门的经理角色，否则他自己既可以审批报销单据，又能进行报销金额的发放，容易出现贪污现象。这就是角色互斥限制。</p>
<p>比如王小明拥有开发部门经理的角色，同时又具有测试部门经理的角色，公司规定一个人最多能担任两个岗位，因此他无法再成为产品部门的经理。这就是角色基数限制。</p>
<p>又比如王小明想要升级为公司的CEO，那么按照公司的规定，他必须先成为公司的技术总监之后，才能进一步升职为CEO。这就是先决条件限制。</p>
<ul>
<li><strong>RBAC3</strong></li>
</ul>
<p>RBAC3其实是RBAC1和RBAC2的合集，也就是它同时具有RBAC1和RBAC2这两个模型的所有特点。我们用一张图来描述一下他们之间的关系：</p>
<p><img src="https://pic4.zhimg.com/80/v2-869b9251135f19bb1efaa0fccc4dde7b_720w.jpg" alt="img"></p>
<p>在RBAC0是基础，在此之上，分别延伸出的RBAC1和RBAC2，它们具有RBAC0的特点，同时又各自拥有自己的特点。而RBAC3则同时拥有RBAC1和RBAC2的所有特点。</p>
<h3 id="2-ACL模型"><a href="#2-ACL模型" class="headerlink" title="2.ACL模型"></a>2.ACL模型</h3><p>针对用户，只要有一个新用户就会授权一次.</p>
<p>比如王小明的公司去了一位新同事，这时公司会给这位新同事分配任务与权限，当这位新同事因为某些原因离职时，公司会收回相应的权限。</p>
<p>与ACL 对比  RBAC不用给用户单个分配权限，只用指向对应的角色就会有对应的权限，而且分配权限和收回权限都很方便。</p>

      
    </div><!-- .entry-content -->

    <footer class="entry-meta">
    <a href="/tefuir/2021/03/11/%E6%9D%83%E9%99%90%E7%AE%A1%E7%90%86/">
    <time datetime="2021-03-11T07:55:30.000Z" class="entry-date">
        2021-03-11
    </time>
</a>
    
    
    </footer>
</article>






  
    <article id="post-Celery" class="post-Celery post type-post status-publish format-standard hentry">
    <!---->

      <header class="entry-header">
        
        
  
    <h1 class="entry-title">
      <a class="article-title" href="/tefuir/2021/03/10/Celery/">Celery</a>
    </h1>
  

        
        <div class="comments-link">
            
            <a href="javascript:void(0);" data-url="https://yanlidoushikeke.gitee.io/myhexo/2021/03/10/Celery/" data-id="ckm3hv60k000064u00cnp3tym" class="leave-reply bdsharebuttonbox" data-cmd="more">Share</a>
        </div><!-- .comments-link -->
      </header><!-- .entry-header -->

    <div class="entry-content">
      
        <h2 id="异步处理Celery"><a href="#异步处理Celery" class="headerlink" title="异步处理Celery"></a>异步处理Celery</h2><p>为什么要使用异步<br>首先，我们要知道计算机的处理分为两种，CPU处理和IO处理。一般来说，处理一个任务，需要CPU和IO相互协调。</p>
<p>CPU的处理速度远远快于IO的，所以当一个任务的CPU处理部分完成后，还需要等待IO的完成。这个等待的过程也就是进程阻塞。进程占着CPU空等没活干。</p>
<p>而异步就是把进程在等待过程中占用的CPU释放掉，让它去处理其他的东西。那正在处理的IO怎么办？系统提供了一种通知机制，使得在IO处理完成后通知系统，该进程进入就绪队列，等待CPU分配。</p>
<p>所以异步使得CPU资源得以充分使用。</p>
<p>一、Celery简介<br>Celery 是一个基于 Python 开发的分布式异步消息任务队列，通过它可以轻松的实现任务的异步处理，</p>
<p>Celery使用场景<br>▷ 异步任务：解放CPU，将耗时的IO任务交给Celery去异步执行，比如发送邮件、音频处理</p>
<p>▷ 定时任务：类似于crontab，比如每日数据统计</p>
<p>Celery构架</p>
<p>▶ Producer：任务委托方</p>
<p>▶ Broker：任务中心（中介），如RabbitMQ、Redis等1</p>
<p>▶ Beat：任务调度器</p>
<p>▶ Worker：任务执行者，可以有多个（分布式）</p>
<p>▶ Result：任务中心的数据库，储存任务执行结果2</p>
<p>▶ Backend：因为任务经由中介，而非直接委派到Worker手上，所以Producer并不知道任务被委派给了谁，以及任务的完成结果，所以这时候需要一个Backend（理解成手机，通过手机查看任务完成情况）</p>
<h4 id="二、Celery的使用"><a href="#二、Celery的使用" class="headerlink" title="二、Celery的使用"></a>二、Celery的使用</h4><h5 id="第一个Celery程序"><a href="#第一个Celery程序" class="headerlink" title="第一个Celery程序"></a>第一个Celery程序</h5><p>新建一个<code>task.py</code></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">from celery import Celery</span><br><span class="line"> </span><br><span class="line">app &#x3D; Celery(&#39;add_task&#39;,</span><br><span class="line">             broker&#x3D;&#39;redis:&#x2F;&#x2F;localhos:6379&#x2F;1&#39;,</span><br><span class="line">             backend&#x3D;&#39;redis:&#x2F;&#x2F;localhost:6379&#x2F;2&#39;) </span><br><span class="line">            </span><br><span class="line">@app.task</span><br><span class="line">def add(x, y):</span><br><span class="line">    print(&quot;running...&quot;,x,y)</span><br><span class="line">    return x+y</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>确定Redis服务已经开启</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">redis-server</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>启动 Celery Worker 来开始监听并执行任务</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">celery -A tasks worker -l info</span><br><span class="line"></span><br><span class="line"># -A 参数表示app名称，-l 参数表示日志类型</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>好了，现在任务给定了，Workers也已经就绪，再新建一个<code>test.py</code>文件</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">from task import add</span><br><span class="line"></span><br><span class="line">result &#x3D; add.delay(2, 3)    # 提交任务，返回任务id</span><br><span class="line">print(result.ready())    # 返回True的话，表示提交成功</span><br><span class="line">print(result.get())    # 获取任务执行结果</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h5 id="Celery配置"><a href="#Celery配置" class="headerlink" title="Celery配置"></a>Celery配置</h5><p>从上面的例子可以看出，每次创建app都需要指定broker和backend。<br>怎样简化这个过程呢？</p>
<p>我们可以将相关的文件模块化（模块名celery_app，再添加<strong>init</strong>.py文件）</p>
<p>模块下新建一个<code>celeryconfig.py</code>文件</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">BROKER_URL &#x3D; &#39;redis:&#x2F;&#x2F;localhos:6379&#x2F;1&#39;</span><br><span class="line"></span><br><span class="line">CELERY_RESULT_BACKEND &#x3D; &#39;redis:&#x2F;&#x2F;localhost:6379&#x2F;2&#39;)</span><br><span class="line"></span><br><span class="line"># 导入指定的任务模块</span><br><span class="line">CELERY_TASKS &#x3D; &#123;</span><br><span class="line">	&#39;celery_app.task1&#39;,    # task1.py是模块下的任务文件</span><br><span class="line">	&#39;celery_app.task2&#39;,</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>


<p><strong>init</strong>.py</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">from celery import Celery</span><br><span class="line"></span><br><span class="line">app &#x3D; Celery(&#39;demo&#39;)</span><br><span class="line"></span><br><span class="line">app.config_from_object(&#39;celery_app.celeryconfig&#39;)</span><br><span class="line"></span><br></pre></td></tr></table></figure>


<p>以上配置完成后，则在task1.py等文件（存在@app.task）中添加一行，即可</p>
<p>from celery_app import app<br>————————————————<br>版权声明：本文为CSDN博主「一剑何风情」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。<br>原文链接：<a target="_blank" rel="noopener" href="https://blog.csdn.net/weixin_37641832/article/details/83218748">https://blog.csdn.net/weixin_37641832/article/details/83218748</a></p>

      
    </div><!-- .entry-content -->

    <footer class="entry-meta">
    <a href="/tefuir/2021/03/10/Celery/">
    <time datetime="2021-03-10T13:35:14.000Z" class="entry-date">
        2021-03-10
    </time>
</a>
    
    
    </footer>
</article>






  
    <article id="post-docker命令" class="post-docker命令 post type-post status-publish format-standard hentry">
    <!---->

      <header class="entry-header">
        
        
  
    <h1 class="entry-title">
      <a class="article-title" href="/tefuir/2021/03/03/docker%E5%91%BD%E4%BB%A4/">docker命令</a>
    </h1>
  

        
        <div class="comments-link">
            
            <a href="javascript:void(0);" data-url="https://yanlidoushikeke.gitee.io/myhexo/2021/03/03/docker%E5%91%BD%E4%BB%A4/" data-id="ckm3hv60v000164u0cq8fev2x" class="leave-reply bdsharebuttonbox" data-cmd="more">Share</a>
        </div><!-- .comments-link -->
      </header><!-- .entry-header -->

    <div class="entry-content">
      
        
      
    </div><!-- .entry-content -->

    <footer class="entry-meta">
    <a href="/tefuir/2021/03/03/docker%E5%91%BD%E4%BB%A4/">
    <time datetime="2021-03-03T01:49:25.000Z" class="entry-date">
        2021-03-03
    </time>
</a>
    
    
    </footer>
</article>






  
    <article id="post-钉钉自定义机器人接入" class="post-钉钉自定义机器人接入 post type-post status-publish format-standard hentry">
    <!---->

      <header class="entry-header">
        
        
  
    <h1 class="entry-title">
      <a class="article-title" href="/tefuir/2021/03/02/%E9%92%89%E9%92%89%E8%87%AA%E5%AE%9A%E4%B9%89%E6%9C%BA%E5%99%A8%E4%BA%BA%E6%8E%A5%E5%85%A5/">钉钉自定义机器人接入</a>
    </h1>
  

        
        <div class="comments-link">
            
            <a href="javascript:void(0);" data-url="https://yanlidoushikeke.gitee.io/myhexo/2021/03/02/%E9%92%89%E9%92%89%E8%87%AA%E5%AE%9A%E4%B9%89%E6%9C%BA%E5%99%A8%E4%BA%BA%E6%8E%A5%E5%85%A5/" data-id="cklrqny3i0000kku020wq70r1" class="leave-reply bdsharebuttonbox" data-cmd="more">Share</a>
        </div><!-- .comments-link -->
      </header><!-- .entry-header -->

    <div class="entry-content">
      
        <h3 id="钉钉自定义机器人接入"><a href="#钉钉自定义机器人接入" class="headerlink" title="钉钉自定义机器人接入"></a>钉钉自定义机器人接入</h3><p>首先明确一点，钉钉自定义机器人早就不支持在手机端创建了，所以打开你的pc端或者mac端的钉钉客户端，在需要机器人的聊天群界面，点击智能群助手</p>
<p>​    随后点击添加机器人按钮</p>
<p>​    此时能看到很多已经封装好的第三方机器人，本次我们选择自定义机器人</p>
<p>​    值得一提的是，钉钉的机器人基于webhook协议，webhook呢是一个api概念,是微服务api的使用范式之一,也被成为反向api,即前端不主动发送请求,完全由后端推送，有机会会单门写一篇文章阐述webhook</p>
<p>​    在添加机器人界面里，填写一些机器人的信息</p>
<p>​    <img src="https://v3u.cn/v3u/Public/js/editor/attached/image/20200229/20200229123553_39070.png" alt="img"></p>
<p>​    需要注意的是，在安全设置一栏里，我们选择加签的方式来验证，在此说明一下，钉钉机器人的安全策略有三种，第一种是使用关键字，就是说你推送的消息里必须包含你创建机器人时定义的关键字，如果不包含就推送不了消息，第二种就是使用加密签名，第三种是定义几个ip源，非这些源的请求会被拒绝，综合来看还是第二种又安全又灵活。</p>
<p>​    <img src="https://v3u.cn/v3u/Public/js/editor/attached/image/20200229/20200229123910_82164.png" alt="img"></p>
<p>​    </p>
<p>​    创建成功后，系统会分配给你一个webhook地址，这个地址需要保存一下，地址中有唯一的accesstoken</p>
<p>​    ok，那么怎么利用这个地址让你的机器人推送消息呢？查看官方文档：<a target="_blank" rel="noopener" href="https://ding-doc.dingtalk.com/doc#/serverapi2/qf2nxq">https://ding-doc.dingtalk.com/doc#/serverapi2/qf2nxq</a>  </p>
<p>​    发现文档居然还是python2.0的版本，好吧，我们自己来翻译成3.0</p>
<p>​    实现代码如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">import time</span><br><span class="line">import hmac</span><br><span class="line">import hashlib</span><br><span class="line">import base64</span><br><span class="line">import urllib.parse</span><br><span class="line"></span><br><span class="line">timestamp &#x3D; str(round(time.time() * 1000))</span><br><span class="line">secret &#x3D; &#39;SEC90485937c351bfaed41fea8eda5f1e155bbf22842d5f9d6871999e05822fd894&#39;</span><br><span class="line">secret_enc &#x3D; secret.encode(&#39;utf-8&#39;)</span><br><span class="line">string_to_sign &#x3D; &#39;&#123;&#125;\n&#123;&#125;&#39;.format(timestamp, secret)</span><br><span class="line">string_to_sign_enc &#x3D; string_to_sign.encode(&#39;utf-8&#39;)</span><br><span class="line">hmac_code &#x3D; hmac.new(secret_enc, string_to_sign_enc, digestmod&#x3D;hashlib.sha256).digest()</span><br><span class="line">sign &#x3D; urllib.parse.quote(base64.b64encode(hmac_code))</span><br><span class="line"># print(timestamp)</span><br><span class="line"># print(sign)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">import requests,json   #导入依赖库</span><br><span class="line">headers&#x3D;&#123;&#39;Content-Type&#39;: &#39;application&#x2F;json&#39;&#125;   #定义数据类型</span><br><span class="line">webhook &#x3D; &#39;https:&#x2F;&#x2F;oapi.dingtalk.com&#x2F;robot&#x2F;send?access_token&#x3D;f0ca7636f5812fe4815c97a72de9a7cc780c414c258b6c9a631036b1d0f49e3b&amp;timestamp&#x3D;&#39;+timestamp+&quot;&amp;sign&#x3D;&quot;+sign</span><br><span class="line">#定义要发送的数据</span><br><span class="line">#&quot;at&quot;: &#123;&quot;atMobiles&quot;: &quot;[&#39;&quot;+ mobile + &quot;&#39;]&quot;</span><br><span class="line">data &#x3D; &#123;</span><br><span class="line">    &quot;msgtype&quot;: &quot;text&quot;,</span><br><span class="line">    &quot;text&quot;: &#123;&quot;content&quot;: &#39;都谁没加到群里来？小心升不了班&#39;&#125;,</span><br><span class="line">    &quot;isAtAll&quot;: True&#125;</span><br><span class="line">res &#x3D; requests.post(webhook, data&#x3D;json.dumps(data), headers&#x3D;headers)   #发送post请求</span><br><span class="line"></span><br><span class="line">print(res.text)</span><br></pre></td></tr></table></figure>

<p>​    </p>

      
    </div><!-- .entry-content -->

    <footer class="entry-meta">
    <a href="/tefuir/2021/03/02/%E9%92%89%E9%92%89%E8%87%AA%E5%AE%9A%E4%B9%89%E6%9C%BA%E5%99%A8%E4%BA%BA%E6%8E%A5%E5%85%A5/">
    <time datetime="2021-03-02T07:58:06.000Z" class="entry-date">
        2021-03-02
    </time>
</a>
    
    
    </footer>
</article>






  
  
    <nav id="pagination">
      <nav id="page-nav">
        <a class="extend prev" rel="prev" href="/tefuir/">&amp;laquo; Prev</a><a class="page-number" href="/tefuir/">1</a><span class="page-number current">2</span><a class="page-number" href="/tefuir/page/3/">3</a><a class="page-number" href="/tefuir/page/4/">4</a><a class="extend next" rel="next" href="/tefuir/page/3/">Next &amp;raquo;</a>
      </nav>
    </nav>
  

</div></div>
        <div id="secondary" class="widget-area" role="complementary">
  
    <aside id="search" class="widget widget_search"><form role="search" method="get" accept-charset="utf-8" id="searchform" class="searchform" action="//google.com/search">
    <div>
        <input type="text" value="" name="s" id="s" />
        <input type="submit" id="searchsubmit" value="搜索" />
    </div>
</form></aside>
  
    
  
    
  
    
  <aside class="widget">
    <h3 class="widget-title">Recents</h3>
    <div class="widget-content">
      <ul>
        
          <li>
            <a href="/tefuir/2021/04/26/python%E5%9F%BA%E7%A1%80%E7%90%86%E8%AE%BA%E5%AE%9D%E5%85%B8/">python基础理论宝典</a>
          </li>
        
          <li>
            <a href="/tefuir/2021/04/23/python3-Flask%E7%BB%93%E5%90%88Socket-io%E7%AE%80%E5%8D%95%E5%AE%9E%E7%8E%B0%E5%9C%A8%E7%BA%BF%E5%AE%A2%E6%9C%8D%E7%B3%BB%E7%BB%9F/">python3+Flask结合Socket.io简单实现在线客服系统</a>
          </li>
        
          <li>
            <a href="/tefuir/2021/04/23/win10%E7%B3%BB%E7%BB%9F%E4%B8%8B%E5%88%A9%E7%94%A8docker%E9%83%A8%E7%BD%B2gunicorn-Flask%E6%89%93%E9%80%A0%E7%8B%AC%E7%AB%8B%E9%95%9C%E5%83%8F/">win10系统下利用docker部署gunicorn+Flask打造独立镜像</a>
          </li>
        
          <li>
            <a href="/tefuir/2021/04/23/win10%E7%B3%BB%E7%BB%9F%E4%B8%8B%E5%9F%BA%E4%BA%8Edocker%E9%85%8D%E7%BD%AEelasticsearch%E9%85%8D%E5%90%88python3%E8%BF%9B%E8%A1%8C%E5%85%A8%E6%96%87%E6%A3%80%E7%B4%A2/">win10系统下基于docker配置elasticsearch配合python3进行全文检索</a>
          </li>
        
          <li>
            <a href="/tefuir/2021/04/23/%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%97%AE%E9%A2%98/">服务器问题</a>
          </li>
        
      </ul>
    </div>
  </aside>

  
    
  
    
  
</div>
      </div>
      <footer id="colophon" role="contentinfo">
    <p>&copy; 2021 南辞
    All rights reserved.</p>
    <p>Powered by <a href="http://hexo.io/" target="_blank">Hexo</a></p>
</footer>
    <script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"1","bdMiniList":false,"bdPic":"","bdStyle":"2","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='/js/share.js'];</script>

<script src="/js/jquery-3.3.1.min.js"></script>


  
<link rel="stylesheet" href="/tefuir/fancybox/jquery.fancybox.css">

  
<script src="/tefuir/fancybox/jquery.fancybox.pack.js"></script>




<script src="/tefuir/js/script.js"></script>


<script src="/js/navigation.js"></script>

<div id="bg"></div>

  </div>
</body>
</html>